# -*- coding: UTF-8 -*-
import asyncio
import subprocess
import sys
from pathlib import Path
from multiping import MultiPing
from netmiko import ConnectHandler
import re
import logging
import os
from PySide6.QtGui import QColor, QFont, QIcon, QPixmap, QImage, QIntValidator, QDesktopServices
from PySide6.QtWidgets import (QMainWindow, QTableWidget, QTableWidgetItem, \
    QHBoxLayout, QLineEdit, QMessageBox, QCheckBox, QComboBox, QPushButton, \
    QDialog, QWidget, QVBoxLayout, QLabel, QListWidget, QSpinBox, QProgressBar, QFrame, \
    QScrollArea, QDialogButtonBox, QGroupBox, QSizeGrip)  # 添加 QSizeGrip 到 QtWidgets 导入
from PySide6.QtCore import Qt, QTimer, QBuffer, QThread, Signal, QUrl, QMetaObject, Q_ARG
from datetime import datetime
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import Alignment, Border, Side
from PySide6.QtWidgets import QFileDialog, QMessageBox
import ipaddress
import socket
import nmap
import threading
import sqlite3
import os.path
import aiohttp
from collections import deque
import concurrent.futures
import jwt

from Icon import icons
from Deviceinfo.device_operations import import_devices, show_add_device_dialog
from Deviceinfo.device_details import show_device_details
from Deviceinfo.device_edit import edit_device
from Deviceinfo.device_delete import delete_device, delete_single_device, toggle_select_all_devices
from Deviceinfo.column_selection import save_column_selection, load_column_selection
from Deviceinfo.monitor_config import MonitorConfig
from Deviceinfo.column_selection import ColumnSelectionDialog
from Netconfig.base_window import BaseWindow

# 配置日志记录器
# logging.basicConfig(
#     level=logging.DEBUG,  # 设置日志级别为DEBUG
#     format='%(asctime)s - %(levelname)s - %(message)s',  # 设置日志格式
#     handlers=[
#         logging.FileHandler('device_info.log'),  # 将日志输出到文件
#         logging.StreamHandler()  # 将日志输出到控制台
#     ]
# )

# 替换为从数据库加载配置的代码
def load_config_from_db():
    """从数据库加载命令和正则映射配置"""
    # 确保目录存在
    db_dir = "device_config/config_file"
    Path(db_dir).mkdir(parents=True, exist_ok=True)
    
    db_path = f"{db_dir}/device_info.db"
    config = {"command_mapping": {}, "regex_mapping": {}}
    
    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()
        
        # 确保表存在
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS devices_regex (
                vendor TEXT,
                command TEXT,
                command_content TEXT,
                regex TEXT,
                PRIMARY KEY (vendor, command)
            )
        """)
        
        cursor.execute("SELECT vendor, command, command_content, regex FROM devices_regex")
        rows = cursor.fetchall()
        
        for row in rows:
            vendor, command, command_content, regex = row
            
            # 初始化vendor字典
            if vendor not in config["command_mapping"]:
                config["command_mapping"][vendor] = {}
            if vendor not in config["regex_mapping"]:
                config["regex_mapping"][vendor] = {}
            
            # 填充数据
            config["command_mapping"][vendor][command] = command_content
            config["regex_mapping"][vendor][command] = regex
            
        conn.commit()
        conn.close()
        return config
    except Exception as e:
        print(f"加载配置失败: {e}")
        return {"command_mapping": {}, "regex_mapping": {}}

# 加载配置
config = load_config_from_db()
command_mapping = config['command_mapping']
regex_mapping = config['regex_mapping']

# 在DeviceInfoWindow类之前添加这个函数
def validate_ip_range(ip_range):
    """
    验证IP地址范围的格式是否正确
    支持以下格式:
    - 单个IP: 192.168.1.1
    - CIDR格式: 192.168.1.0/24
    - IP范围: 192.168.1.1-192.168.1.254
    """
    try:
        # 检查是否是CIDR格式
        if '/' in ip_range:
            ipaddress.ip_network(ip_range, strict=False)
            return True
        
        # 检查是否是IP范围格式
        elif '-' in ip_range:
            start_ip, end_ip = ip_range.split('-')
            ipaddress.ip_address(start_ip.strip())
            ipaddress.ip_address(end_ip.strip())
            return True
            
        # 检查是否是单个IP
        else:
            ipaddress.ip_address(ip_range)
            return True
            
    except ValueError:
        return False

class PingThread(QThread):
    ping_result = Signal(tuple)  # 修改为单个设备的结果

    def __init__(self, devices, indices, batch_size=1, parent=None):
        super().__init__(parent)
        self.devices = devices
        self.indices = indices
        self.batch_size = batch_size
        self._is_running = True
        self.parent = parent
        self.indices = [i for i in self.indices if 0 <= i < len(devices)]

    def run(self):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(self.async_ping())
        loop.close()

    async def async_ping(self):
        if not self.devices:  # 检查设备列表是否为空
            return
        for i in range(0, len(self.indices), self.batch_size):
            if not self._is_running:
                break
            batch_indices = self.indices[i:i + self.batch_size]
            valid_batch_indices = [j for j in batch_indices if 0 <= j < len(self.devices)]
            batch = [self.devices[j] for j in valid_batch_indices]
            tasks = [self.ping_device(valid_batch_indices[index], device) for index, device in enumerate(batch)]
            results = await asyncio.gather(*tasks)
            for result in results:
                self.ping_result.emit(result)  # 发出单个设备的结果

    async def ping_device(self, index, device):
        logging.debug(f"开始 ping 设备: 索引={index}, IP={device['ip']}")
        try:
            mp = MultiPing([device["ip"]])
            mp.send()
            responses, no_responses = mp.receive(1)
            if responses:
                self.devices[index]["online"] = "在线"
                if self.parent:
                    self.parent.save_devices()
                    self.parent.update_table()
                try:
                    version, cpu, memory, uptime, fan, power, temperature = await self.get_device_info(device)
                    return index, "在线", version, cpu, memory, uptime, fan, power, temperature
                except Exception as e:
                    logging.error(f"获取设备信息失败: {e}")
                    return index, "在线", "", "", "", "", "", "", ""
            else:
                logging.debug(f"Ping 设备 {device['ip']} 失败: 无响应")
                return index, "离线", "", "", "", "", "", "", ""
        except (asyncio.TimeoutError, OSError) as e:
            logging.error(f"Ping 设备 {device['ip']} 失败: {e}")
            return index, "离线", "", "", "", "", "", "", ""

    async def get_device_info(self, device):
        try:
            # 确保端口号有效
            try:
                port = int(device.get("port", "22"))  # 如果没有端口号，默认使用22
            except (TypeError, ValueError):
                port = 22  # 如果转换失败，使用默认端口

            # 根据协议设置默认端口
            if device.get("protocol", "ssh").lower() == "telnet":
                port = 23 if port == 22 else port

            device_info = await fetch_device_info({
                **device,
                "port": port  # 使用处理后的端口号
            })
            return device_info
        except Exception as e:
            logging.error(f"获取设备信息失败: {e}")
            raise  # 重新抛出异常，让调用者处理

    def stop(self):
        self._is_running = False
        self.wait()


async def fetch_device_info(device):
    try:
        device_type_mapping = {
            "华为": "huawei",
            "华三": "hp_comware",
            "思科": "cisco_ios",
            "Juniper": "juniper",
            "锐捷": "ruijie",
            "中兴": "zte",
        }
        
        # 确保端口号有效
        try:
            port = int(device.get("port", "22"))  # 如果没有端口号，默认使用22
        except (TypeError, ValueError):
            port = 22  # 如果转换失败，使用默认端口

        # 根据协议设置设备类型和端口
        protocol = device.get("protocol", "ssh").lower()
        base_device_type = device_type_mapping.get(device["vendor"], "generic")
        
        if protocol == "telnet":
            device_type = f"{base_device_type}_telnet"
            port = 23 if port == 22 else port
        else:
            device_type = base_device_type
            
        try:
            net_connect = ConnectHandler(
                device_type=device_type,
                ip=device["ip"],
                username=device["username"],
                password=device["password"],
                secret=device.get("enable_password", ""),
                port=port,
                fast_cli=True,
                global_delay_factor=0.05,
                timeout=5,
                session_timeout=30,
                blocking_timeout=5,
                keepalive=30,
                auto_connect=True,
            )
        except Exception as conn_error:
            # 如果SSH连接失败，尝试使用Telnet
            if protocol == "ssh" and "SSHException" in str(conn_error):
                logging.warning(f"SSH连接失败，尝试使用Telnet连接: {device['ip']}")
                device_type = f"{base_device_type}_telnet"
                net_connect = ConnectHandler(
                    device_type=device_type,
                    ip=device["ip"],
                    username=device["username"],
                    password=device["password"],
                    secret=device.get("enable_password", ""),
                    port=23,  # 使用Telnet默认端口
                    fast_cli=True,
                    global_delay_factor=2.0,  # Telnet需要更长的延迟
                    timeout=10,
                    session_timeout=30,
                    blocking_timeout=8,
                )
            else:
                raise

        net_connect.enable()

        # 从数据库加载设备的命令和正则表达式
        vendor_commands = command_mapping.get(device["vendor"], {})
        vendor_regex = regex_mapping.get(device["vendor"], {})

        if not vendor_commands or not vendor_regex:
            logging.error(f"未找到{device['vendor']}的命令或正则表达式配置")
            return "", "", "", "", "", "", ""

        try:
            # 获取版本信息
            version_output = net_connect.send_command(vendor_commands["version"])
            version_match = re.search(vendor_regex["version"], version_output)
            version = version_match.group(1) if version_match else ""

            # 获取 CPU 使用率
            cpu_output = net_connect.send_command(vendor_commands["cpu"])
            cpu_match = re.search(vendor_regex["cpu"], cpu_output)
            cpu = f"{cpu_match.group(1)}%" if cpu_match else ""

            # 获取内存使用情况
            memory_output = net_connect.send_command(vendor_commands["memory"])
            memory_match = re.search(vendor_regex["memory"], memory_output)
            if memory_match:
                if device["vendor"] == "思科":
                    try:
                        total = int(memory_match.group(1))
                        used = int(memory_match.group(2))
                        if total > 0:
                            memory_percent = (used / total) * 100
                            memory = f"{memory_percent:.1f}%"
                        else:
                            memory = ""
                    except (ValueError, TypeError) as e:
                        logging.error(f"内存计算错误: {e}")
                        memory = ""
                elif device["vendor"] == "华三":
                    try:
                        free_ratio = float(memory_match.group(1))
                        memory_percent = 100 - free_ratio  # 用100%减去空闲比例得到使用率
                        memory = f"{memory_percent:.1f}%"
                    except (ValueError, TypeError) as e:
                        logging.error(f"华三设备内存计算错误: {e}")
                        memory = ""
                else:
                    memory = f"{memory_match.group(1)}%"
            else:
                memory = ""

            # 获取运行时间
            uptime_output = net_connect.send_command(vendor_commands["uptime"])
            uptime_match = re.search(vendor_regex["uptime"], uptime_output)
            logging.debug(f"运行时间原始输出: {uptime_output}")
            logging.debug(f"运行时间正则匹配结果: {uptime_match}")

            if uptime_match:
                # 直接从正则匹配组中获取各个时间值
                weeks = int(uptime_match.group(1) or 0)
                days = int(uptime_match.group(2) or 0)
                hours = int(uptime_match.group(3) or 0)
                minutes = int(uptime_match.group(4) or 0)
                
                logging.debug(f"解析结果: 周={weeks}, 天={days}, 小时={hours}, 分钟={minutes}")
                
                # 计算总天数和小时数
                total_days = weeks * 7 + days
                total_hours = hours + (minutes // 60)  # 将分钟转换为小时
                
                logging.debug(f"计算结果: 总天数={total_days}, 总小时数={total_hours}")
                
                # 格式化输出
                if total_days > 0 or total_hours > 0:
                    uptime = f"{total_days}天{total_hours}小时"
                else:
                    uptime = "0天0小时"
                
                logging.debug(f"最终运行时间结果: {uptime}")
            else:
                uptime = "0天0小时"
                logging.warning(f"无法匹配运行时间，原始输出: {uptime_output}")

            # 获取风扇状态
            fan_output = net_connect.send_command(vendor_commands["fan"])
            fan_match = re.search(vendor_regex["fan"], fan_output)
            fan = fan_match.group(1) if fan_match else ""

            # 获取电源状态
            power_output = net_connect.send_command(vendor_commands["power"])
            power_match = re.search(vendor_regex["power"], power_output)
            power = power_match.group(1) if power_match else ""

            # 获取温度状态
            temperature_output = net_connect.send_command(vendor_commands["temperature"])
            temperature_match = re.search(vendor_regex["temperature"], temperature_output)
            temperature = temperature_match.group(1) if temperature_match else ""

        except Exception as e:
            logging.error(f"获取{device['vendor']}设备信息时出错: {e}")
            version, cpu, memory, uptime, fan, power, temperature = "", "", "", "", "", "", ""

        net_connect.disconnect()
        return version, cpu, memory, uptime, fan, power, temperature

    except Exception as e:
        logging.error(f"获取设备信息失败: {e}")
        return "", "", "", "", "", "", ""


async def main(devices):
    tasks = [fetch_device_info(device) for device in devices]
    results = await asyncio.gather(*tasks)
    return results


class DeviceInfoWindow(BaseWindow):
    # 添加新的信号
    update_device_signal = Signal(dict)  # 用于更新单个设备信息
    batch_update_signal = Signal(list)   # 用于批量更新设备信息
    
    def __init__(self, parent=None):
        super().__init__(parent)
        # 添加数据库锁，需要放在最开始的位置
        self.db_lock = threading.Lock()
        
        # 添加分页相关属性
        self.page_size = 50  # 每页显示的设备数量
        self.current_page = 1  # 当前页码
        self.filtered_indices = []  # 初始化筛选索引
        
        # 创建主窗口组件
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QVBoxLayout(self.central_widget)
        
        # 创建过滤器布局 - 移到表格前面
        self.filter_layout = QHBoxLayout()
        self.layout.addLayout(self.filter_layout)
        
        # 添加厂商过滤器
        self.vendor_filter_label = QLabel("厂商:")
        self.filter_layout.addWidget(self.vendor_filter_label)
        self.vendor_filter_combo = QComboBox()
        self.vendor_filter_combo.addItems(
            ["全部", "华为", "华三", "思科", "Juniper", "锐捷", "中兴", "迈普", "Dell"])
        self.vendor_filter_combo.currentTextChanged.connect(self.apply_filter)
        self.filter_layout.addWidget(self.vendor_filter_combo)
        
        # 添加设备类型过滤器
        self.device_type_filter_label = QLabel("设备类型:")
        self.filter_layout.addWidget(self.device_type_filter_label)
        self.device_type_filter_combo = QComboBox()
        self.device_type_filter_combo.addItems(
            ["全部", "交换机", "路由器", "防火墙", "负载均衡", "VPN", "AC控制器", "其他设备"])
        self.device_type_filter_combo.currentTextChanged.connect(self.apply_filter)
        self.filter_layout.addWidget(self.device_type_filter_combo)
        
        # 添加主机名过滤器
        self.host_filter_label = QLabel("主机名:")
        self.filter_layout.addWidget(self.host_filter_label)
        self.host_filter_input = QLineEdit()
        self.host_filter_input.setPlaceholderText("比如:SW01")
        self.host_filter_input.textChanged.connect(self.apply_filter)
        self.filter_layout.addWidget(self.host_filter_input)
        
        # 添加IP地址过滤器
        self.ip_filter_label = QLabel("IP地址:")
        self.filter_layout.addWidget(self.ip_filter_label)
        self.ip_filter_input = QLineEdit()
        self.ip_filter_input.setPlaceholderText("比如:192.168.1.1")
        self.ip_filter_input.textChanged.connect(self.apply_filter)
        self.filter_layout.addWidget(self.ip_filter_input)
        
        # 添加在线状态过滤器
        self.online_status_filter_label = QLabel("在线状态:")
        self.filter_layout.addWidget(self.online_status_filter_label)
        self.online_status_filter_combo = QComboBox()
        self.online_status_filter_combo.addItems(["全部", "在线", "离线"])
        self.online_status_filter_combo.currentTextChanged.connect(self.apply_filter)
        self.filter_layout.addWidget(self.online_status_filter_combo)
        
        # 添加自选列按钮
        self.select_columns_button = QPushButton("自选列")
        self.select_columns_button.setIcon(
            QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.edit_icon_base64))))
        )
        # 修改这一行，将 load_column_selection 改为 show_column_selection
        self.select_columns_button.clicked.connect(self.show_column_selection)
        self.filter_layout.addWidget(self.select_columns_button)
        
        # 添加监控项按钮
        self.custom_monitor_button = QPushButton("监控项")
        self.custom_monitor_button.setIcon(
            QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.monitor_icon))))
        )
        self.custom_monitor_button.clicked.connect(self.edit_monitor_config)
        self.filter_layout.addWidget(self.custom_monitor_button)
        
        # 创建表格
        self.table = QTableWidget()
        self.table.setSelectionBehavior(QTableWidget.SelectRows)
        self.table.setSelectionMode(QTableWidget.SingleSelection)
        self.table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table.verticalHeader().setVisible(False)
        
        # 设置表格列和表头
        self.column_keys = [
            "select",        # 选择列
            "vendor",        # 厂商
            "device_type",   # 设备类型
            "model",         # 型号
            "host",         # 主机名
            "ip",           # IP地址
            "remote_type",  # 远程类型
            "protocol",     # 协议
            "port",         # 端口
            "username",     # 用户名
            "password",     # 密码
            "enable_password", # Enable密码
            "online",       # 在线状态
            "version",      # 版本
            "cpu",          # CPU
            "memory",       # 内存
            "uptime",       # 启动时长
            "fan",          # 风扇
            "power",        # 电源
            "temperature",  # 温度
            "update_time",  # 更新时间
            "operation"     # 操作
        ]
        
        # 设置表格列数
        self.table.setColumnCount(len(self.column_keys))
        
        # 设置表头
        headers = [
            "选择", "厂商", "设备类型", "型号", "主机名", "IP地址", 
            "远程类型", "协议", "端口", "用户名", "密码", "Enable密码",
            "在线状态", "版本", "CPU", "内存", "启动时长",
            "风扇", "电源", "温度", "更新时间", "操作"
        ]
        self.layout.addWidget(self.table)
        self.table.setAlternatingRowColors(True)    # 设置表格的替颜色
        self.table.horizontalHeader().setStyleSheet("QHeaderView::section {background-color: #00B050; color: white; border: 0.5px solid #D8D8D8; font-size: 12px;}")
        self.table.setHorizontalHeaderLabels(headers)
        
        # 添加表格到布局
        self.layout.addWidget(self.table)
        
        # 其他初始化代码...
        self.monitor_config = MonitorConfig(self)
        self.monitor_config.load_config_data()
        self.config_data = {}
        self.ping_thread = None
        self.devices = []
        self.current_filters = {
            "vendor": "",
            "device_type": "",
            "host": "",
            "ip": "",
            "online_status": ""
        }
        
        # 添加主题相关属性
        self.current_theme = "initial"  # 默认主题
        
        # 设置窗口标题
        self.setWindowTitle("设备信息")
        
        # 创建所有按钮
        self.create_buttons()
        
        # 加载设备列表
        self.load_devices()
        
        # 在所有初始化完成后检查权限
        self.check_admin_permissions()

        self.timer = QTimer()
        self.timer.timeout.connect(self.start_ping_thread)
        self.last_device_info = {}
        self.load_column_selection()

        # 第一次运行时不等待五分钟
        self.first_run = True
        self.start_ping_thread()

        # 添加自定义时间设置
        self.custom_time_layout = QHBoxLayout()

        self.time_label = QLabel("轮巡时间:")
        self.custom_time_layout.addWidget(self.time_label)

        self.time_combo = QComboBox()
        self.time_combo.addItems(["5分钟", "10分钟", "15分钟", "自定义"])
        self.time_combo.currentTextChanged.connect(self.set_timer_from_combo)
        self.custom_time_layout.addWidget(self.time_combo)

        self.export_button = QPushButton("导出")
        self.export_button.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.export)))))
        self.export_button.setCursor(Qt.PointingHandCursor)
        self.export_button.clicked.connect(self.export_to_excel)
        self.bottom_buttons_layout.addWidget(self.export_button)

        self.custom_time_input = QLineEdit()
        self.custom_time_input.setPlaceholderText("输入钟数")
        self.custom_time_input.setMaxLength(3)
        self.custom_time_input.setValidator(QIntValidator())
        self.custom_time_input.setEnabled(False)  # 初始时禁用
        self.custom_time_input.textChanged.connect(self.validate_custom_time)
        self.custom_time_layout.addWidget(self.custom_time_input)
        self.custom_time_layout.addStretch(1)
        # self.custom_time_layout.addSpacing(50)  # 空白占位符

        # 将轮巡时间设置放在 "SecureCRT设置" 按钮的右边
        self.bottom_buttons_layout.addLayout(self.custom_time_layout)

        # 默认选择5分钟
        self.time_combo.setCurrentIndex(0)

        self.filtered_indices = []
        self.load_devices()
        self.timer = QTimer()
        self.timer.timeout.connect(self.start_ping_thread)
        self.last_device_info = {}
        self.load_column_selection()

        self.first_run = True
        self.start_ping_thread()

        # 加载外部样式表
        self.load_styles()

        # 添加一个专门用于自动导出的定时器
        self.export_timer = QTimer()
        self.export_timer.timeout.connect(self.perform_auto_export)

        # 确保数据库目录存在
        Path("device_config/config_file").mkdir(parents=True, exist_ok=True)
        
        # 初始化数据库连接
        self.db_path = "device_config/config_file/device_info.db"
        self.init_config_db()
        
        # 加载配置
        self.load_config()
        
        # 优化表格性能
        self.table.setVerticalScrollMode(QTableWidget.ScrollPerPixel)
        self.table.setHorizontalScrollMode(QTableWidget.ScrollPerPixel)
        self.table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table.setSelectionBehavior(QTableWidget.SelectRows)
        self.table.setSelectionMode(QTableWidget.SingleSelection)
        self.table.verticalHeader().setVisible(False)
        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.setShowGrid(False)  # 可选：隐藏网格线以提高性能

        # 添加设备信息队列
        self.device_queue = deque()
        self.is_processing = False
        
        # 连接信号到槽
        self.update_device_signal.connect(self.update_single_device)
        self.batch_update_signal.connect(self.update_device_batch)
        
        # 创建事件循环
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)
        
        # 创建线程池
        self.thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=4)
    
        # 在创建表格后，加载保存的列选择设置
        self.load_saved_column_selection()
    
        # 添加权限检查方法
        self.check_admin_permissions()
    
        # 添加大小调整控件
        self._size_grip = QSizeGrip(self)
        self._size_grip.setFixedSize(20, 20)
        
        # 在布局末尾添加一个水平布局来放置 size grip
        bottom_layout = QHBoxLayout()
        bottom_layout.addStretch()  # 添加弹性空间
        bottom_layout.addWidget(self._size_grip)  # 添加大小调整控件
        self.layout.addLayout(bottom_layout)

    def check_admin_permissions(self):
        """检查管理员权限并更新按钮状态"""
        # 向上查找具有 settings 属性的父窗口
        parent = self.parent()
        token = None
        while parent:
            if hasattr(parent, 'settings'):
                token = parent.settings.value('user_token')
                break
            if hasattr(parent, 'parent') and callable(parent.parent):
                parent = parent.parent()
            else:
                break
            
        is_admin = False
        if token:
            try:
                user_info = jwt.decode(token, 'your-secret-key', algorithms=['HS256'])
                is_admin = user_info.get('role') == '管理员'
            except (jwt.ExpiredSignatureError, jwt.InvalidTokenError) as e:
                pass
        
        # 更新按钮状态 - 移除 setEnabled(False) 的调用
        self.select_all_button.setEnabled(True)  # 允许所有用户使用全选功能
        
        # 只控制管理员权限按钮
        self.batch_delete_btn.setEnabled(is_admin)
        self.import_btn.setEnabled(is_admin)
        self.add_btn.setEnabled(is_admin)
        # self.port_config_btn.setEnabled(is_admin)
        
        # 设置提示信息
        if not is_admin:
            self.batch_delete_btn.setToolTip("需要管理员权限")
            self.import_btn.setToolTip("需要管理员权限")
            self.add_btn.setToolTip("需要管理员权限")
            # self.port_config_btn.setToolTip("需要管理员权限")
        else:
            self.batch_delete_btn.setToolTip("")
            self.import_btn.setToolTip("")
            self.add_btn.setToolTip("")
            # self.port_config_btn.setToolTip("")

    def batch_delete(self):
        """批量删除设备"""
        if not self.check_is_admin():
            QMessageBox.warning(self, "权限不足", "只有管理员可以批量删除设备,请点击右上角的个人中心登录")
            return
        # ... 原有的批量删除代码 ...

    def import_devices(self):
        """导入设备"""
        if not self.check_is_admin():
            QMessageBox.warning(self, "权限不足", "只有管理员可以导入设备,请点击右上角的个人中心登录")
            return
        # ... 原有的导入设备代码 ...


    def add_device(self):
        """添加设备"""
        if not self.check_is_admin():
            QMessageBox.warning(self, "权限不足", "只有管理员可以添加设备,请点击右上角的个人中心登录")
            return
        # ... 原有的添加设备代码 ...


    # def port_config(self):
    #     """端口配置"""
    #     if not self.check_is_admin():
    #         QMessageBox.warning(self, "权限不足", "只有管理员可以配置端口,请点击右上角的个人中心登录")
    #         return
        # ... 原有的端口配置代码 ...


    def check_is_admin(self):
        """检查当前用户是否是管理员"""
        # 向上查找具有 settings 属性的父窗口
        parent = self.parent()
        token = None
        while parent:
            if hasattr(parent, 'settings'):
                token = parent.settings.value('user_token')
                break
            if hasattr(parent, 'parent') and callable(parent.parent):
                parent = parent.parent()
            else:
                break
        else:  # 如果没有找到具有 settings 的父窗口
            return False
        
        if not token:
            return False
        
        try:
            user_info = jwt.decode(token, 'your-secret-key', algorithms=['HS256'])
            return user_info.get('role') == '管理员'
        except (jwt.ExpiredSignatureError, jwt.InvalidTokenError):
            return False

    async def process_device_queue(self):
        """异步处理设备队列"""
        while self.device_queue:
            batch = []
            # 每次处理最多10个设备
            for _ in range(10):
                if not self.device_queue:
                    break
                batch.append(self.device_queue.popleft())
            
            if batch:
                await self.process_device_batch(batch)
    
    async def process_device_batch(self, devices):
        """异步处理设备批次"""
        tasks = []
        async with aiohttp.ClientSession() as session:
            for device in devices:
                task = asyncio.create_task(self.async_check_device(session, device))
                tasks.append(task)
            
            results = await asyncio.gather(*tasks, return_exceptions=True)
            valid_results = [r for r in results if isinstance(r, dict)]
            
            if valid_results:
                # 使用信号发送批量更新
                self.batch_update_signal.emit(valid_results)
    
    async def async_check_device(self, session, device):
        """异步检查单个设备状态"""
        try:
            # 使用 asyncio 的 subprocess 进行 ping 检测
            proc = await asyncio.create_subprocess_exec(
                'ping', '-n', '1', '-w', '1000', device['ip'],
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            
            stdout, stderr = await proc.communicate()
            is_online = proc.returncode == 0
            
            device_info = {
                'ip': device['ip'],
                'online': "在线" if is_online else "离线",
                'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            }
            
            if is_online:
                # 异步获取设备详细信息
                try:
                    device_details = await self.async_get_device_info(device)
                    device_info.update(device_details)
                except Exception as e:
                    logging.error(f"获取设备详情失败: {e}")
            
            return device_info
            
        except Exception as e:
            logging.error(f"检查设备状态失败: {e}")
            return None
    
    def update_single_device(self, device_info):
        """更新单个设备信息"""
        if not device_info:
            return
            
        for i, device in enumerate(self.devices):
            if device['ip'] == device_info['ip']:
                self.devices[i].update(device_info)
                break
        
        # 仅更新变化的行
        self.update_table_row(device_info)
    
    def update_device_batch(self, device_batch):
        """批量更新设备信息"""
        if not device_batch:
            return
            
        # 创建IP到更新信息的映射
        updates = {d['ip']: d for d in device_batch}
        
        # 批量更新设备列表
        for i, device in enumerate(self.devices):
            if device['ip'] in updates:
                self.devices[i].update(updates[device['ip']])
        
        # 更新表格显示
        self.update_table()
    
    def update_table_row(self, device_info):
        """只更新单行数据"""
        for row in range(self.table.rowCount()):
            ip_item = self.table.item(row, 5)  # IP列的索引
            if ip_item and ip_item.text() == device_info['ip']:
                self.update_table_cell(row, device_info)
                break
    
    def update_table_cell(self, row, device_info):
        """更新表格单元格"""
        # 更新在线状态
        status_item = QTableWidgetItem()
        if device_info['online'] == "在线":
            status_item.setIcon(self.online_icon)
            status_item.setText("在线")
            status_item.setForeground(QColor("#008B00"))
        else:
            status_item.setIcon(self.offline_icon)
            status_item.setText("离线")
            status_item.setForeground(QColor("#B22222"))
        self.table.setItem(row, 6, status_item)  # 在线状态列
        
        # 更新其他信息
        for col, key in enumerate(self.column_keys[7:-1], 7):  # 跳过选择、基本信息和操作列
            if key in device_info:
                item = QTableWidgetItem(str(device_info[key]))
                item.setTextAlignment(Qt.AlignCenter)
                if key in ["cpu", "memory"]:
                    self.set_background_color(item, device_info[key])
                self.table.setItem(row, col, item)

    def init_config_db(self):
        """初始化配置数据库"""
        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            # 创建配置表（如果不存在）
            cursor.execute('''
                CREATE TABLE IF NOT EXISTS devices_regex (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    vendor TEXT NOT NULL,
                    command TEXT NOT NULL,
                    command_content TEXT NOT NULL,
                    regex TEXT NOT NULL,
                    UNIQUE(vendor, command)
                )
            ''')
            
            # 检查是否需要插入默认配置
            cursor.execute("SELECT COUNT(*) FROM devices_regex")
            if cursor.fetchone()[0] == 0:
                default_config = {
                    "command_mapping": {
                        "思科": {
                            "version": "show version",
                            "cpu": "show processes cpu | include CPU utilization",
                            "memory": "show memory statistics | include Processor",
                            "uptime": "show version | include uptime",
                            "fan": "show env all",
                            "power": "show env power",
                            "temperature": "show env all"
                        },
                        "华为": {
                            "version": "display version",
                            "cpu": "display cpu-usage",
                            "memory": "display memory-usage",
                            "uptime": "display version | include uptime",
                            "fan": "display device fan",
                            "power": "display device power",
                            "temperature": "display device temperature"
                        },
                        "华三": {
                            "version": "display version",
                            "cpu": "display cpu-usage",
                            "memory": "display memory",
                            "uptime": "display version | include uptime",
                            "fan": "display fan",
                            "power": "display power",
                            "temperature": "display environment"
                        },
                        "中兴": {
                            "version": "show version",
                            "cpu": "show cpu-usage",
                            "memory": "show memory",
                            "uptime": "show version | include uptime",
                            "fan": "show fan",
                            "power": "show power",
                            "temperature": "show temperature"
                        },
                        "锐捷": {
                            "version": "show version",
                            "cpu": "show cpu-usage",
                            "memory": "show memory usage",
                            "uptime": "show version | include uptime",
                            "fan": "show fan",
                            "power": "show power",
                            "temperature": "show temperature"
                        },
                        "Juniper": {
                            "version": "show version",
                            "cpu": "show chassis routing-engine",
                            "memory": "show chassis routing-engine",
                            "uptime": "show system uptime",
                            "fan": "show chassis environment",
                            "power": "show chassis power",
                            "temperature": "show chassis temperature"
                        }
                    },
                    "regex_mapping": {
                        "思科": {
                            "version": r"Version\s+(\S+)",
                            "cpu": r"CPU utilization.+?:\s+(\d+)%",
                            "memory": r"Processor\s+\w+\s+(\d+)\s+(\d+)\s+(\d+)",
                            "uptime": r"uptime is (.*?)(?=\n|$)",  # 修改为匹配整行
                            "fan": r"FAN\s+is\s+(\w+)",
                            "power": r"Status\s+.*?\s+(Good)",
                            "temperature": r"TEMPERATURE\s+is\s+(\w+)"
                        },
                        "华为": {
                            "version": r"VRP.+?Version\s+(\S+)",
                            "cpu": r"CPU Usage\s*:\s*(\d+)%",
                            "memory": r"Memory utilizing rate is\s*(\d+)%",
                            "uptime": r"uptime is (.*?)(?=\n|$)",  # 修改为匹整行
                            "fan": r"FanStatus\s*:\s*(\w+)",
                            "power": r"PowerStatus\s*:\s*(\w+)",
                            "temperature": r"Temperature\s*:\s*(\d+)"
                        },
                        "华三": {
                            "version": r"Software Version\s+(\S+)",
                            "cpu": r"CPU Usage\s*:\s*(\d+)%",
                            "memory": r"Memory Usage\s*:\s*(\d+)%",
                            "uptime": r"uptime is (?:(\d+) weeks?, )?(?:(\d+) days?, )?(?:(\d+) hours?, )?(?:(\d+) minutes?)?",
                            "fan": r"Fan\s+\d+\s+State:\s*(\w+)",
                            "power": r"Power\s+\d+\s+State:\s*(\w+)",
                            "temperature": r"Temperature\s*:\s*(\d+)"
                        },
                        "中兴": {
                            "version": r"Software\s+Version\s+(\S+)",
                            "cpu": r"CPU utilization\s*:\s*(\d+)%",
                            "memory": r"Memory utilization\s*:\s*(\d+)%",
                            "uptime": r"uptime is (?:(\d+) weeks?, )?(?:(\d+) days?, )?(?:(\d+) hours?, )?(?:(\d+) minutes?)?",
                            "fan": r"Fan\s+status\s*:\s*(\w+)",
                            "power": r"Power\s+status\s*:\s*(\w+)",
                            "temperature": r"Temperature\s*:\s*(\d+)"
                        },
                        "锐捷": {
                            "version": r"Software\s+Version\s+(\S+)",
                            "cpu": r"CPU Usage\s*:\s*(\d+)%",
                            "memory": r"Memory Usage\s*:\s*(\d+)%",
                            "uptime": r"uptime is (?:(\d+) weeks?, )?(?:(\d+) days?, )?(?:(\d+) hours?, )?(?:(\d+) minutes?)?",
                            "fan": r"Fan\s+status\s*:\s*(\w+)",
                            "power": r"Power\s+status\s*:\s*(\w+)",
                            "temperature": r"Temperature\s*:\s*(\d+)"
                        },
                        "Juniper": {
                            "version": r"Junos:\s+(\S+)",
                            "cpu": r"CPU utilization\s*:\s*(\d+)\s*percent",
                            "memory": r"Memory utilization\s*:\s*(\d+)\s*percent",
                            "uptime": r"System booted: (.*?)(?=\n|$)",  # 修改为匹配整行
                            "fan": r"Fan\s+Status:\s*(\w+)",
                            "power": r"Power\s+Status:\s*(\w+)",
                            "temperature": r"Temperature:\s*(\d+)"
                        }
                    }
                }
                
                # 插入所有配置到数据库
                for vendor in default_config["command_mapping"]:
                    for command, command_content in default_config["command_mapping"][vendor].items():
                        regex = default_config["regex_mapping"].get(vendor, {}).get(command, "")
                        cursor.execute(
                            "INSERT INTO devices_regex (vendor, command, command_content, regex) VALUES (?, ?, ?, ?)",
                            (vendor, command, command_content, regex)
                        )
            
            conn.commit()
            conn.close()
        except Exception as e:
            QMessageBox.warning(self, "错误", f"初始化配置数据库失败: {e}")

    def load_config(self):
        """从数据库加载配置"""
        try:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            cursor.execute("SELECT vendor, command, command_content, regex FROM devices_regex")
            rows = cursor.fetchall()
            
            self.config = {"command_mapping": {}, "regex_mapping": {}}
            
            for row in rows:
                vendor, command, command_content, regex = row
                
                if vendor not in self.config["command_mapping"]:
                    self.config["command_mapping"][vendor] = {}
                if vendor not in self.config["regex_mapping"]:
                    self.config["regex_mapping"][vendor] = {}
                
                self.config["command_mapping"][vendor][command] = command_content
                self.config["regex_mapping"][vendor][command] = regex
            
            conn.close()
        except Exception as e:
            QMessageBox.warning(self, "错误", f"加载配置失败: {e}")

    def load_styles(self):
        with open('device_config/config_file/styles.css', 'r', encoding='utf-8') as f:
            style = f.read()
        self.setStyleSheet(style)

    def export_to_excel(self):
        # 创建一个消框
        msg_box = QMessageBox(self)
        msg_box.setWindowTitle("导出选项")
        msg_box.setText("选择导出类型:\n手动导出: 手动导出当前设备信息\n自动导出: 根据设置的轮巡时间自动导出")

        # 设置按钮
        manual_button = msg_box.addButton("手动导出", QMessageBox.ActionRole)
        auto_button = msg_box.addButton("自动导出", QMessageBox.ActionRole)
        cancel_button = msg_box.addButton("取消", QMessageBox.RejectRole)

        # 显示消息框
        msg_box.exec_()

        # 获取用户选择的按钮
        if msg_box.clickedButton() == manual_button:
            # 手动导出
            self.perform_export()
        elif msg_box.clickedButton() == auto_button:
            # 自动导出
            self.setup_auto_export()
        else:
            # 取消操作
            return

    def perform_export(self):
        # 获取当前时间并格式化为字符串
        current_time = datetime.now().strftime("%Y-%m-%d_%H-%M")

        # 指定保存路径 - 使用绝对路径
        export_directory = os.path.abspath("device_config/devices_export")  # 转换为绝对路径

        # 创建完整文件名
        file_name = os.path.join(export_directory, f"devicesinfo_manual_{current_time}.xlsx")

        # 检查目录是否存在，如果不存在则创建
        if not os.path.exists(export_directory):
            os.makedirs(export_directory)  # 创建目录

        try:
            # 调用导出逻辑，传递所选择的文件名
            self.export_logic(file_name)  # 导出逻辑调用

            # 创建一个带有自定义按钮的消息框
            msg_box = QMessageBox(self)
            msg_box.setWindowTitle("导出成功")
            msg_box.setText(f"设备信息已成功导出为:\n{file_name}")
            msg_box.setIcon(QMessageBox.Information)

            # 添加按钮
            open_button = msg_box.addButton("打开文件", QMessageBox.ActionRole)
            open_folder_button = msg_box.addButton("打开所在文件夹", QMessageBox.ActionRole)
            close_button = msg_box.addButton("关闭", QMessageBox.RejectRole)

            # 显示消息框
            msg_box.exec_()

            # 根据用户点击的按钮执行相应操作
            if msg_box.clickedButton() == open_button:
                # 使用系统默程序打开文件
                if sys.platform == "win32":
                    os.startfile(os.path.normpath(file_name))
                elif sys.platform == "darwin":  # macOS
                    subprocess.run(["open", file_name])
                else:  # linux
                    subprocess.run(["xdg-open", file_name])
            elif msg_box.clickedButton() == open_folder_button:
                # 打开文件所在文件夹
                if sys.platform == "win32":
                    # 使用绝对路径并规范化路径分隔符
                    normalized_path = os.path.normpath(file_name)
                    subprocess.run(["explorer", "/select,", normalized_path])
                elif sys.platform == "darwin":  # macOS
                    subprocess.run(["open", "-R", file_name])
                else:  # linux
                    subprocess.run(["xdg-open", export_directory])

        except Exception as e:
            print(f"导出过程中出现错误: {e}")  # 记录异常
            self.show_message(f"导出失败: {e}")

    def setup_auto_export(self):
        selected_time = self.time_combo.currentText()

        # 先停止之前的导出定时器（如果存在
        if self.export_timer.isActive():
            self.export_timer.stop()

        if selected_time == "自定义":
            if self.custom_time_input.text().strip():
                try:
                    minutes = int(self.custom_time_input.text())
                    interval = minutes * 60 * 1000  # 转换为毫秒

                    # 使用专门的导出定时
                    self.export_timer.start(interval)
                    QMessageBox.information(self, "成功", f"已设置为每{minutes}分钟自动导出！")
                except ValueError:
                    QMessageBox.warning(self, "错误", "自定义分钟数无效，请输入有效的数字！")
                    return
            else:
                QMessageBox.warning(self, "警告", "请在自定义输入框中输入实数的分钟数！")
                return

        else:
            try:
                minutes = int(selected_time.replace("分钟", ""))
                interval = minutes * 60 * 1000

                # 使用专门的导出定时器
                self.export_timer.start(interval)
                QMessageBox.information(self, "成功", f"已设置为每{minutes}分钟自动导出！")
            except ValueError:
                QMessageBox.warning(self, "错误", "获取的时间设置无效，请检查轮巡时间设置！")

    def perform_auto_export(self):
        # 获取当前时间并格式化
        current_time = datetime.now().strftime("%Y-%m-%d_%H-%M")

        # 指定保存路径
        export_directory = "device_config/devices_export"  # 相对路径

        # 创建完整文件名
        file_name = f"devicesinfo_auto_{current_time}.xlsx"
        file_path = os.path.join(export_directory, file_name)  # 组合路径

        # 检查目录是否存在，如果不存在则创建
        if not os.path.exists(export_directory):
            os.makedirs(export_directory)  # 创建目录

        try:
            # 调用导出逻辑，传递所选择的文件名
            self.export_logic(file_path)  # 导出逻辑调用
        except Exception as e:
            print(f"导出过程中出现错误: {e}")  # 记录异常

    def export_logic(self, file_name):
        try:
            # 获取当示的列
            visible_columns = [self.table.horizontalHeaderItem(i).text() for i in range(self.table.columnCount()) if 
                             not self.table.isColumnHidden(i)]

            # 映射表头到设备字段
            headers_to_fields = {
                # "选择": "select",
                "厂商": "vendor",
                "设备类型": "device_type", 
                "型号": "model",
                "主机名": "host",
                "IP地址": "ip",
                "远程类型": "remote_type",
                "协议": "protocol",
                "端口": "port",
                "用户名": "username",
                "密码": "password",
                "Enable密码": "enable_password",
                "在线状态": "online",
                "版本": "version",
                "CPU": "cpu",
                "内存": "memory",
                "启动时长": "uptime",
                "风扇": "fan",
                "电源": "power",
                "温度": "temperature",
                "更新时间": "update_time"
                # 移除 "操作" 列,因为它包含按钮而不是数据
            }

            # 过滤掉 "操作" 、"选择" 列
            
            visible_columns = [col for col in visible_columns if col != "选择"]
            visible_columns = [col for col in visible_columns if col != "操作"]

            # 获取需要导出的列对应的字段
            export_fields = [headers_to_fields[header] for header in visible_columns if header in headers_to_fields]

            # 获取选中的设备
            selected_devices = []
            for row in range(self.table.rowCount()):
                checkbox_widget = self.table.cellWidget(row, 0)
                if checkbox_widget and checkbox_widget.layout().itemAt(0).widget().isChecked():
                    # 获取当前行对应的设备
                    device_ip = self.table.item(row, 5).text()  # IP地址在第6列
                    device = next((d for d in self.devices if d["ip"] == device_ip), None)
                    if device:
                        selected_devices.append(device)

            # 如果没有选中任何设备,提示用户并返回
            if not selected_devices:
                QMessageBox.warning(self, "警告", "请至少选择一个设备进行导出!")
                raise Exception("未选择任何设备")

            # 提取选中设备的相关信息
            data = []
            for device in selected_devices:
                row_data = []
                for field in export_fields:
                    if field == "select":
                        row_data.append("")  # 为选择列添加空值
                    else:
                        row_data.append(device.get(field, ""))
                data.append(row_data)

            # 使用 pandas 创建 DataFrame 并导出为 Excel 文件
            df = pd.DataFrame(data, columns=visible_columns)
            df.to_excel(file_name, index=False, engine='openpyxl')

            # 加载导出的 Excel 文件
            wb = load_workbook(file_name)
            ws = wb.active

            # 设置单元格水平、垂直居中
            for row in ws.iter_rows(min_row=1, max_col=ws.max_column, max_row=ws.max_row):
                for cell in row:
                    cell.alignment = Alignment(horizontal='center', vertical='center')

            # 设置边框
            thin_border = Border(left=Side(style='thin'),
                               right=Side(style='thin'),
                               top=Side(style='thin'),
                               bottom=Side(style='thin'))
            for row in ws.iter_rows(min_row=1, max_col=ws.max_column, max_row=ws.max_row):
                for cell in row:
                    cell.border = thin_border

            # 自适应列宽
            for column_cells in ws.columns:
                length = max(len(str(cell.value)) for cell in column_cells)
            ws.column_dimensions[column_cells[0].column_letter].width = length + 2

            # 保存改后 Excel 文件
            wb.save(file_name)

        except Exception as e:
            logging.error(f"导出到 Excel 时出错: {e}")
            raise

    def edit_monitor_config(self):
        self.monitor_config.edit_monitor_config()

    def some_method_that_saves_config(self):
        # 当需要保存时调
        self.monitor_config.save_all_mappings(self.monitor_config.config_data)


    def edit_data(self):
        default_file = "device_config/devices.xlsx"
        if not Path(default_file).exists():
            self.show_message(f"默认文件 {default_file} 不存在")
            return

        try:
            # 使用默认的 Excel 应用程序开文件
            if sys.platform == "win32":
                subprocess.Popen(["start", "", default_file], shell=True)
            elif sys.platform == "darwin":
                subprocess.Popen(["open", default_file])
            else:
                subprocess.Popen(["xdg-open", default_file])
            self.show_message(f"已打开 {default_file}, 请在设备信息的Sheet中进行编辑")
        except Exception as e:
            self.show_message(f"打开文件时出错: {e}")

    def show_message(self, message):
        # 使用 QMessageBox 显消息
        QMessageBox.information(self, "信息", message)

    def start_ping_thread(self, priority_indices=None):
        if not self.devices:  # 检查设备表是否为空
            return
        
        if self.ping_thread is not None:
            self.ping_thread.stop()
        
        # 如果有优先检测的设备索引,则优先检测这些设备
        if priority_indices:
            indices = priority_indices
        else:
            indices = self.filtered_indices if self.filtered_indices else list(range(len(self.devices)))
        
        self.ping_thread = PingThread(self.devices, indices, batch_size=1)
        self.ping_thread.ping_result.connect(self.handle_ping_results)

        # 使用 QTimer 延迟启动 ping 线程
        QTimer.singleShot(5000, self.ping_thread.start)  # 延迟2秒启动

        if self.first_run:
            self.first_run = False
            self.timer.start(300000)  # 5分钟后开始计时

    def set_timer_from_combo(self, text):
        if text == "自定义":
            self.custom_time_input.setEnabled(True)
            self.validate_custom_time()
        else:
            self.custom_time_input.setEnabled(False)
            numeric_part = ''.join(filter(str.isdigit, text))
            minutes = int(numeric_part)
            self.set_timer(minutes * 60 * 1000)

    def set_timer(self, interval):
        self.timer.stop()
        self.timer.start(interval)

    def validate_custom_time(self):
        if self.time_combo.currentText() == "自定义":
            try:
                minutes = int(self.custom_time_input.text())
                self.set_timer(minutes * 60 * 1000)
            except ValueError:
                pass

    def handle_ping_results(self, result):
        if len(result) != 9:  # 检查结果长度
            logging.error(f"结果不完整: {result}")
            return
        index, status, version, cpu, memory, uptime, fan, power, temperature = result
        update_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')  # 格式化时间
        if index < 0 or index >= len(self.devices):  # 查索引是否有效
            logging.error(f"无效的索引: {index}")
            return
        self.devices[index]["online"] = status
        self.devices[index]["version"] = version
        self.devices[index]["cpu"] = cpu
        self.devices[index]["memory"] = memory
        self.devices[index]["uptime"] = uptime
        self.devices[index]["fan"] = fan
        self.devices[index]["power"] = power
        self.devices[index]["temperature"] = temperature
        self.devices[index]["update_time"] = update_time  # 添加更新时间
        self.save_devices()
        self.apply_filter()  # 在更新设备信息后重新应用筛选

    def closeEvent(self, event):    
        # 关闭窗口时停止所有线程和定时器
        if self.ping_thread is not None:
            self.ping_thread.stop()
        self.timer.stop()
        if hasattr(self, 'export_timer'):
            self.export_timer.stop()
        event.accept()

    def save_column_selection_to_db(self):     # 1. 定义需要保存的列选择信息
        # 直接定义需要保存的列选择信息
        selected_columns = [
            "选择",
            "厂商",
            "设备类型",
            "型号",
            "主机名",
            "IP地址",
            "在线状态",
            "版本",
            "CPU",
            "内存",
            "启动时长",
            "风扇",
            "电源",
            "温度",
            "更新时间",
            "操作"
        ]

        # 2. 连接到 SQLite 数据库
        conn = sqlite3.connect('device_config/config_file/device_info.db', timeout=10)
        cursor = conn.cursor()

        # 3. 创建 column_selection 表（果尚未存在）
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS column_selection (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                column_name TEXT NOT NULL
            )
        ''')

        # 4. 清空表格内容，然后插入新的列选择信息
        cursor.execute('DELETE FROM column_selection')  # 清空原先的列选择
        for column_name in selected_columns:
            cursor.execute('INSERT INTO column_selection (column_name) VALUES (?)', (column_name,))

        # 提交事务
        conn.commit()

        # 5. 关闭数据库连接
        cursor.close()
        conn.close()

    def load_column_selection(self):
        load_column_selection(self.table)

    def show_column_selection_dialog(self):
        dialog = QDialog(self)
        dialog.setWindowTitle("自选列")
        layout = QVBoxLayout()
        layout.addWidget(QLabel("选择要显示的列:"))
        checkboxes = [QCheckBox(self.table.horizontalHeaderItem(i).text()) for i in range(self.table.columnCount())]
        for i, checkbox in enumerate(checkboxes):
            checkbox.setChecked(not self.table.isColumnHidden(i))
            layout.addWidget(checkbox)
        apply_button = QPushButton("应用")
        apply_button.clicked.connect(lambda: self.apply_column_selection(checkboxes, dialog))
        layout.addWidget(apply_button)
        dialog.setLayout(layout)
        dialog.exec()

    def apply_column_selection(self, checkboxes, dialog):
        selected_columns = []
        for i, checkbox in enumerate(checkboxes):
            self.table.setColumnHidden(i, not checkbox.isChecked())
            if checkbox.isChecked():
                selected_columns.append(self.table.horizontalHeaderItem(i).text())

        # 保存选中的列到数据库
        save_column_selection(selected_columns)
        dialog.accept()

    def save_devices(self):
        """保存设备信息到数据库"""
        with self.db_lock:  # 使用锁保护数据库操作
            conn = None
            try:
                # 连接到 SQLite 数据库
                conn = sqlite3.connect('device_config/config_file/device_info.db', timeout=20)
                cursor = conn.cursor()

                # 创建 deviceinfo 表（如果尚未存在）
                cursor.execute('''
                    CREATE TABLE IF NOT EXISTS deviceinfo (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        vendor TEXT NOT NULL,
                        device_type TEXT NOT NULL,
                        model TEXT,
                        host TEXT NOT NULL,
                        ip TEXT NOT NULL,
                        remote_type TEXT,
                        protocol TEXT,
                        port TEXT,
                        username TEXT NOT NULL,
                        password TEXT NOT NULL,
                        enable_password TEXT,
                        online TEXT,
                        version TEXT,
                        cpu TEXT,
                        memory TEXT,
                        uptime TEXT,
                        fan TEXT,
                        power TEXT,
                        temperature TEXT,
                        update_time TEXT
                    )
                ''')

                # 开始事务
                conn.execute('BEGIN')
                
                # 清空原先的设备数据
                cursor.execute('DELETE FROM deviceinfo')

                # 插入新的设备数据
                for device in self.devices:
                    cursor.execute('''
                        INSERT INTO deviceinfo (vendor, device_type, model, host, ip, remote_type, protocol, port, 
                                             username, password, enable_password, online, version, cpu, memory, 
                                             uptime, fan, power, temperature, update_time)
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    ''', (
                        device["vendor"], device["device_type"], device.get("model"), device["host"],
                        device["ip"], device["remote_type"], device["protocol"], device["port"],
                        device["username"], device["password"], device.get("enable_password"), device["online"],
                        device["version"], device["cpu"], device["memory"], device["uptime"],
                        device["fan"], device["power"], device["temperature"], device.get("update_time")
                    ))

                # 更新 port_device_info 表
                self.update_port_device_info(cursor)

                # 提交事务
                conn.commit()
                
            except sqlite3.Error as e:
                logging.error(f"保存设备数据时出错: {e}")
                if conn:
                    conn.rollback()  # 发生错误时回滚事务
                raise
            finally:
                if conn:
                    conn.close()

    def load_devices(self):
        """从数据库加载设备信息"""
        try:
            with sqlite3.connect('device_config/config_file/device_info.db') as conn:
                cursor = conn.cursor()
                cursor.execute("""
                    SELECT vendor, device_type, model, host, ip, remote_type, 
                           protocol, port, username, password, enable_password,
                           online, version, cpu, memory, uptime, fan, power, 
                           temperature, update_time
                    FROM deviceinfo
                """)
                
                self.devices = []
                for row in cursor.fetchall():
                    device = {
                        'vendor': row[0],
                        'device_type': row[1],
                        'model': row[2],
                        'host': row[3],
                        'ip': row[4],
                        'remote_type': row[5],
                        'protocol': row[6],
                        'port': row[7],
                        'username': row[8],
                        'password': row[9],
                        'enable_password': row[10],
                        'online': row[11],
                        'version': row[12],
                        'cpu': row[13],
                        'memory': row[14],
                        'uptime': row[15],
                        'fan': row[16],
                        'power': row[17],
                        'temperature': row[18],
                        'update_time': row[19]
                    }
                    self.devices.append(device)
                
                self.update_table()
                
        except sqlite3.Error as e:
            QMessageBox.critical(self, "错误", f"加载设备信息失败: {str(e)}")

    def update_port_device_info(self, cursor):  # 更新端口设备信息
        try:
            # 获取设备信息
            cursor.execute(
                'SELECT vendor, device_type, model, host, ip, remote_type, username, password, enable_password FROM deviceinfo')
            rows = cursor.fetchall()

            # 清空 port_device_info 表
            cursor.execute('DELETE FROM port_device_info')

            # 将从 deviceinfo 表中读取的数据插入到 port_device_info 表
            for row in rows:
                cursor.execute('''
                    INSERT INTO port_device_info 
                    (vendor, device_type, model, hostname, ip_address, remote_type, username, password, secret)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
                    ''', (row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]))

            # 提交事务
            cursor.connection.commit()
            
        except sqlite3.Error as e:
            logging.error(f"更新端口设备信息时出错: {e}")
            cursor.connection.rollback()  # 发生错误时回滚事务
            raise

    def is_ip_address_exist(self, ip):
        return any(device["ip"] == ip for device in self.devices)

    def add_device_from_dialog(self, dialog):   # 从对话添加设备
        vendor = self.vendor_combo.currentText()
        device_type = self.device_type.currentText()
        host = self.host_input.text()
        ip = self.ip_input.text()
        remote_type = self.remote_type.currentText()
        protocol = self.protocol_combo.currentText()
        port = self.port_combo.currentText()
        username = self.username_input.text()
        password = self.password_input.text()
        enable_password = self.enable_password_input.text() if vendor.lower() not in ["华为", "华三"] else ""
        model = self.model_input.text()
        if not all([vendor, device_type, host, ip, remote_type, protocol, port, username, password]):
            QMessageBox.warning(self, "警告", "所有字段都必须填写")
            return
        if self.is_ip_address_exist(ip):
            QMessageBox.warning(self, "警告", f"IP地址 {ip} 已存在，无法重复添加")
            return
        self.devices.append({
            "vendor": vendor,
            "device_type": device_type,
            "model": model,
            "host": host,
            "ip": ip,
            "remote_type": remote_type,
            "protocol": protocol,
            "port": port,
            "username": username,
            "password": password,
            "enable_password": enable_password,
            "online": "离线",
            "version": "",
            "cpu": "",
            "memory": "",
            "uptime": "",
            "fan": "",
            "power": "",
            "temperature": ""
        })
        self.save_devices()
        self.update_table()
        self.last_device_info = {
            "vendor": vendor,
            "device_type": device_type,
            "model": model,
            "host": host,
            "ip": ip,
            "remote_type": remote_type,
            "protocol": protocol,
            "port": port,
            "username": username,
            "password": password,
            "enable_password": enable_password,
            "online": "离线",
            "version": "",
            "cpu": "",
            "memory": "",
            "uptime": "",
            "fan": "",
            "power": "",
            "temperature": ""
        }
        QMessageBox.information(self, "设备加成", f"设备 {host} ({ip}) 已成功添加")
        self.save_devices()  # 添加设备后保

        # 立即检测新添加设备的在线状态
        new_device_index = len(self.devices) - 1
        self.start_ping_thread([new_device_index])

    def device_exists(self, host, ip):  # 检查设备是否存在代码...
        for device in self.devices:
            if device["host"] == host and device["ip"] == ip:
                return True
        return False

    def update_table(self):
        """更新表格显示"""
        # 暂时阻塞表格的重绘和排序
        self.table.setUpdatesEnabled(False)
        self.table.setSortingEnabled(False)
        
        try:
            # 保存当前选中状态
            selected_ips = set()
            for row in range(self.table.rowCount()):
                checkbox_widget = self.table.cellWidget(row, 0)
                if checkbox_widget and checkbox_widget.layout().itemAt(0).widget().isChecked():
                    ip = self.table.item(row, 5).text()  # IP地址在第6列
                    selected_ips.add(ip)
            
            # 获取要显示的设备列表并更新分页信息
            devices_to_show = self.update_page_info()
            
            # 获取当前页的设备
            current_page_devices = self.get_current_page_devices(devices_to_show)
            
            # 预先创建所有需要的部件
            checkboxes = []
            operation_widgets = []
            
            for device in current_page_devices:
                # 创建复选框
                checkbox = QCheckBox()
                checkbox_layout = QHBoxLayout()
                checkbox_layout.addWidget(checkbox)
                checkbox_layout.setAlignment(Qt.AlignCenter)
                checkbox_container = QWidget()
                checkbox_container.setLayout(checkbox_layout)
                # 恢复选中状态
                if device["ip"] in selected_ips:
                    checkbox.setChecked(True)
                checkboxes.append(checkbox_container)
                
                # 创建操作按钮
                operation_layout = QHBoxLayout()
                operation_layout.setSpacing(2)  # 减小按钮间距
                operation_layout.setContentsMargins(2, 2, 2, 2)  # 减小边距
                
                buttons = []
                for btn_text, obj_name in [("编辑", "edit-button"), ("删除", "delete-button"), 
                                          ("详情", "details-button")]:
                    button = QPushButton(btn_text)
                    button.setObjectName(obj_name)
                    button.setCursor(Qt.PointingHandCursor)
                    button.setFixedHeight(24)  # 固定按钮高度
                    button.setFixedWidth(45)   # 固定按钮宽度
                    buttons.append(button)
                    operation_layout.addWidget(button)
                
                operation_container = QWidget()
                operation_container.setLayout(operation_layout)
                operation_widgets.append((operation_container, buttons))
            
            # 一次性设置表格行数
            self.table.setRowCount(len(current_page_devices))
            
            # 获取当前用户角色
            current_user = None
            # 安全地获取 settings
            main_window = self.parent()
            if main_window and hasattr(main_window, 'settings'):
                token = main_window.settings.value('user_token')
                if token:
                    try:
                        current_user = jwt.decode(token, 'your-secret-key', algorithms=['HS256'])
                    except jwt.ExpiredSignatureError:
                        pass
            
            for row, device in enumerate(current_page_devices):
                # 设置复选框
                self.table.setCellWidget(row, 0, checkboxes[row])
                
                # 更新其他列
                for col, key in enumerate(self.column_keys[1:-1], 1):
                    item = QTableWidgetItem()
                    if key == "online":
                        # 特殊处理在线状态列
                        if device["online"] == "在线":
                            icon_data = bytes.fromhex(icons.online_icon_base64)
                            item.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(icon_data))))
                            item.setText("在线")
                            item.setForeground(QColor("#008B00"))
                        else:
                            icon_data = bytes.fromhex(icons.offline_icon_base64)
                            item.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(icon_data))))
                            item.setText("离线")
                            item.setForeground(QColor("#B22222"))
                    else:
                        item.setText(str(device.get(key, "")))
                        if key in ["cpu", "memory"]:
                            self.set_background_color(item, device.get(key, ""))
                    
                    item.setTextAlignment(Qt.AlignCenter)
                    self.table.setItem(row, col, item)
                
                # 设置操作按钮
                operation_container, buttons = operation_widgets[row]
                edit_btn, delete_btn, details_btn = buttons
                
                edit_btn.clicked.connect(lambda checked, r=row: edit_device(self, r, current_page_devices))
                delete_btn.clicked.connect(lambda checked, r=row: delete_single_device(self, r, current_page_devices))
                # login_btn.clicked.connect(lambda checked, r=row: self.login_to_device(r, current_page_devices))
                details_btn.clicked.connect(lambda checked, r=row: show_device_details(self, r, current_page_devices))
                
                self.table.setCellWidget(row, len(self.column_keys)-1, operation_container)
                
                # 根据用户角色控制密码显示
                password_column_index = self.column_keys.index("password") if "password" in self.column_keys else -1
                enable_password_column_index = self.column_keys.index("enable_password") if "enable_password" in self.column_keys else -1
                
                # 处理登录密码
                if password_column_index >= 0:
                    if current_user and current_user.get('role') == '管理员':
                        # 管理员可以看到完整密码
                        password_item = QTableWidgetItem(device.get("password", ""))
                    else:
                        # 未登录或非管理员只能看到掩码
                        password_item = QTableWidgetItem("********")
                    password_item.setTextAlignment(Qt.AlignCenter)
                    self.table.setItem(row, password_column_index, password_item)
                
                # 处理 enable 密码
                if enable_password_column_index >= 0:
                    if current_user and current_user.get('role') == '管理员':
                        # 管理员可以看到完整 enable 密码
                        enable_password_item = QTableWidgetItem(device.get("enable_password", ""))
                    else:
                        # 未登录或非管理员只能看到掩码
                        enable_password_item = QTableWidgetItem("********")
                    enable_password_item.setTextAlignment(Qt.AlignCenter)
                    self.table.setItem(row, enable_password_column_index, enable_password_item)
                
            # 设置行高
            self.table.verticalHeader().setDefaultSectionSize(35)
            
            # 只在必要时调整列宽
            if not hasattr(self, '_columns_resized'):
                self.table.resizeColumnsToContents()
                self._columns_resized = True
                
        finally:
            # 恢复表格更新和排序
            self.table.setSortingEnabled(True)
            self.table.setUpdatesEnabled(True)
            
            # 更新全选按钮状态
            self.update_select_all_button_state()

    def update_select_all_button_state(self):
        """更新全选按钮的状态"""
        all_checked = True
        any_checked = False
        
        for row in range(self.table.rowCount()):
            checkbox_widget = self.table.cellWidget(row, 0)
            if checkbox_widget:
                checkbox = checkbox_widget.layout().itemAt(0).widget()
                if checkbox.isChecked():
                    any_checked = True
                else:
                    all_checked = False
        
        if all_checked:
            self.select_all_button.setText("取消全选")
        else:
            self.select_all_button.setText("全选")

    # def login_to_device(self, row, devices):
    #     if row < 0 or row >= len(devices):
    #         QMessageBox.warning(self, "警告", "选择的设备索引无效")
    #         return

    #     device = devices[row]
    #     # 创建并显示 NetworkGUI 窗口
    #     self.network_gui = NetworkGUI()
    #     self.network_gui.ip_input.setText(device["ip"])
    #     self.network_gui.username_input.setText(device["username"])
    #     self.network_gui.password_input.setText(device["password"])
    #     self.network_gui.device_type.setCurrentText(device["remote_type"])
    #     self.network_gui.protocol.setCurrentText(device["protocol"].capitalize())
    #     self.network_gui.port_input.setText(str(device["port"]))
    #     self.network_gui.show()

    def set_background_color(self, item, usage):
        if not usage:
            return
        try:
            usage_value = int(usage.strip('%'))
            if 30 < usage_value <= 50:
                item.setBackground(QColor("#FFA500"))
                # 如果符合件，字体颜色设置为白色
                item.setForeground(QColor("#FFFFFF"))
            elif 50 < usage_value <= 70:
                item.setBackground(QColor("#FFFF00"))
                # 如果符合条件，字体颜色设置为白色
                # item.setForeground(QColor("#FFFFFF"))
            elif usage_value > 70:
                item.setBackground(QColor("#FF0000"))
                # 如果符合条件，字体颜色设置为白色
                item.setForeground(QColor("#FFFFFF"))
        except ValueError:
            pass

    def show_discovery_dialog(self):
        # 创建一个新的对话框，并设置为独立窗口
        dialog = QDialog(None)  # 将父窗口设置为None，使其完全独立
        dialog.setWindowTitle("网络设备自动发现")
        dialog.setMinimumWidth(400)
        # 设置对话框为独立窗口，并添加所有窗口控制按钮
        dialog.setWindowFlags(
            Qt.Window |  # 设置为独立窗口
            Qt.WindowSystemMenuHint |  # 添加系统菜单
            Qt.WindowMinimizeButtonHint |  # 添加最小化按钮
            Qt.WindowMaximizeButtonHint |  # 添加最大化按钮
            Qt.WindowCloseButtonHint  # 添加关闭按钮
        )
        
        layout = QVBoxLayout()
        
        # 添加nmap下载链接
        nmap_link_layout = QHBoxLayout()
        nmap_label = QLabel("需要安装nmap,安装完成后重启电脑才能使用。")
        nmap_link = QLabel("<a href='https://nmap.org/download.html'>点击下载nmap</a>")
        nmap_link.setOpenExternalLinks(True) # 允许打开外部链接
        nmap_link_layout.addWidget(nmap_label)
        nmap_link_layout.addWidget(nmap_link)
        nmap_link_layout.addStretch()
        layout.addLayout(nmap_link_layout)
        
        # 添加分隔线
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        layout.addWidget(line)
        
        # IP地址范围输入
        ip_layout = QHBoxLayout()
        ip_layout.addWidget(QLabel("IP地址范围:"))
        ip_input = QLineEdit()
        ip_input.setPlaceholderText("例如: 192.168.1.0/24")
        ip_layout.addWidget(ip_input)
        layout.addLayout(ip_layout)

        # 端口范围设置
        port_layout = QHBoxLayout()
        port_layout.addWidget(QLabel("端口范围:"))
        start_port = QSpinBox()
        start_port.setRange(1, 65535)
        start_port.setValue(22)
        port_layout.addWidget(start_port)
        port_layout.addWidget(QLabel("到"))
        end_port = QSpinBox()
        end_port.setRange(1, 65535)
        end_port.setValue(23)
        port_layout.addWidget(end_port)
        layout.addLayout(port_layout)

        # 超时设置
        timeout_layout = QHBoxLayout()
        timeout_layout.addWidget(QLabel("超时时间(秒):"))
        timeout_spin = QSpinBox()
        timeout_spin.setRange(1, 60)
        timeout_spin.setValue(2)  # 减少默认超时时间
        timeout_layout.addWidget(timeout_spin)
        layout.addLayout(timeout_layout)

        # 进度显示
        progress_label = QLabel("准备扫描...")
        layout.addWidget(progress_label)

        # 按钮布局中移除最小化按钮
        button_layout = QHBoxLayout()
        start_button = QPushButton("开始扫描")
        cancel_button = QPushButton("取消")
        button_layout.addWidget(start_button)
        button_layout.addWidget(cancel_button)
        layout.addLayout(button_layout)

        dialog.setLayout(layout)

        class FastScanThread(QThread):
            progress_signal = Signal(str)
            device_found_signal = Signal(dict)
            finished_signal = Signal()
            error_signal = Signal(str)

            def __init__(self, ip_range, start_port, end_port, timeout):
                super().__init__()
                self.ip_range = ip_range
                self.start_port = start_port
                self.end_port = end_port
                self.timeout = timeout
                self.is_running = True

            def run(self):
                try:
                    nm = nmap.PortScanner()
                    network = ipaddress.ip_network(self.ip_range, strict=False)
                    
                    # 使用更详细的扫描参数，包括服务版本检测
                    arguments = f'-sS -sV -T4 -n --min-parallelism 100 --max-parallelism 256 --host-timeout {self.timeout}s -p {self.start_port}-{self.end_port}'
                    
                    self.progress_signal.emit(f"正在扫描网段 {self.ip_range}...")
                    
                    nm.scan(hosts=str(network), arguments=arguments)
                    
                    for host in nm.all_hosts():
                        if not self.is_running:
                            break
                        
                        if nm[host].state() == 'up':
                            open_ports = []
                            services = []
                            if 'tcp' in nm[host]:
                                for port in nm[host]['tcp']:
                                    if nm[host]['tcp'][port]['state'] == 'open':
                                        open_ports.append(str(port))
                                        service = nm[host]['tcp'][port].get('product', '').lower()
                                        if service:
                                            services.append(service)
                            
                            if open_ports:
                                try:
                                    hostname = socket.getfqdn(host)
                                except:
                                    hostname = host
                                    
                                # 根据端口和服务特征判断设备类型
                                device_type = "未知设备"
                                vendor = "未知厂商"
                                
                                # 网络设备特征
                                network_device_ports = {'22', '23', '161', '162', '443', '80'}
                                network_device_services = {'cisco', 'huawei', 'h3c', 'ruijie', 'zte', 'juniper', 'fortinet'}
                                
                                # 服务器特征
                                server_ports = {'80', '443', '3389', '21', '25', '53', '3306', '1433', '5432'}
                                server_services = {'apache', 'nginx', 'iis', 'mysql', 'postgresql', 'mssql', 'exchange'}
                                
                                # PC主机特征
                                pc_ports = {'135', '139', '445', '3389'}
                                pc_services = {'microsoft-ds', 'netbios', 'windows'}
                                
                                # 打印机特征
                                printer_ports = {'515', '631', '9100'}
                                printer_services = {'printer', 'ipp', 'jetdirect'}
                                
                                # 存储设备特征
                                storage_ports = {'2049', '3260', '8080'}
                                storage_services = {'nas', 'san', 'storage', 'synology', 'qnap'}
                                
                                # 判断设备类型
                                open_ports_set = set(open_ports)
                                services_str = ' '.join(services).lower()
                                
                                if any(service in services_str for service in network_device_services) or \
                                   len(network_device_ports & open_ports_set) >= 2:
                                    device_type = "网络设备"
                                    # 进一步判断具体类型
                                    if '23' in open_ports:
                                        device_type = "交换机/路由器"
                                    elif '161' in open_ports or '162' in open_ports:
                                        device_type = "网络管理设备"
                                    
                                    # 判断厂商
                                    for vendor_name in ['cisco', 'huawei', 'h3c', 'ruijie', 'zte', 'juniper']:
                                        if vendor_name in services_str:
                                            vendor = vendor_name.upper()
                                            break
                                
                                elif len(server_ports & open_ports_set) >= 3 or \
                                     any(service in services_str for service in server_services):
                                    device_type = "服务器"
                                    if 'windows' in services_str:
                                        vendor = "Windows Server"
                                    elif 'linux' in services_str or 'ubuntu' in services_str or 'centos' in services_str:
                                        vendor = "Linux Server"
                                
                                elif len(pc_ports & open_ports_set) >= 2 or \
                                     any(service in services_str for service in pc_services):
                                    device_type = "PC主机"
                                    if 'windows' in services_str:
                                        vendor = "Windows PC"
                                    elif 'linux' in services_str:
                                        vendor = "Linux PC"
                                
                                elif len(printer_ports & open_ports_set) >= 1 or \
                                     any(service in services_str for service in printer_services):
                                    device_type = "打印机"
                                    if 'hp' in services_str:
                                        vendor = "HP"
                                    elif 'epson' in services_str:
                                        vendor = "EPSON"
                                
                                elif len(storage_ports & open_ports_set) >= 1 or \
                                     any(service in services_str for service in storage_services):
                                    device_type = "存储设备"
                                    if 'synology' in services_str:
                                        vendor = "Synology"
                                    elif 'qnap' in services_str:
                                        vendor = "QNAP"
                                
                                device_info = {
                                    "vendor": vendor,
                                    "device_type": device_type,
                                    "model": "",
                                    "host": hostname,
                                    "ip": host,
                                    "remote_type": "SSH" if '22' in open_ports else "TELNET",
                                    "protocol": "ssh" if '22' in open_ports else "telnet",
                                    "port": "22" if '22' in open_ports else "23",
                                    "username": "admin",
                                    "password": "admin",
                                    "enable_password": "",
                                    "online": "在线",
                                    "version": "",
                                    "cpu": "",
                                    "memory": "",
                                    "uptime": "",
                                    "fan": "",
                                    "power": "",
                                    "temperature": "",
                                    "update_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                                }
                                
                                self.device_found_signal.emit(device_info)
                                self.progress_signal.emit(f"发现{device_type}: {host} ({hostname})")

                    self.finished_signal.emit()

                except Exception as e:
                    self.error_signal.emit(f"扫描过程出错: {str(e)}")

            def stop(self):
                self.is_running = False

        def add_discovered_device(device_info):
            # 检查IP是否已存在
            if not self.is_ip_address_exist(device_info["ip"]):
                self.devices.append(device_info)
                self.save_devices()
                self.update_table()

        def update_progress(message):
            progress_label.setText(message)

        def show_error(error_msg):
            QMessageBox.warning(dialog, "错误", error_msg)

        scan_thread = None

        def start_scan():
            nonlocal scan_thread
            ip_range = ip_input.text().strip()
            if not validate_ip_range(ip_range):
                QMessageBox.warning(dialog, "错误", "无效的IP地址范围")
                return

            start_button.setEnabled(False)
            progress_label.setText("正在扫描...")

            scan_thread = FastScanThread(
                ip_range,
                start_port.value(),
                end_port.value(),
                timeout_spin.value()
            )
            
            scan_thread.progress_signal.connect(update_progress)
            scan_thread.device_found_signal.connect(add_discovered_device)
            scan_thread.finished_signal.connect(lambda: scan_finished())
            scan_thread.error_signal.connect(show_error)
            
            scan_thread.start()

        def scan_finished():
            start_button.setEnabled(True)
            progress_label.setText("扫描完成")
            QMessageBox.information(dialog, "完成", "扫描完成！新发现的设备已添加到设备列表中。")

        def cancel_scan():
            nonlocal scan_thread
            if scan_thread and scan_thread.isRunning():
                scan_thread.stop()
                scan_thread.wait()
            dialog.reject()

        start_button.clicked.connect(start_scan)
        cancel_button.clicked.connect(cancel_scan)

        dialog.exec()

    def toggle_select_all(self):
        """切换全选/取消全选状态"""
        # 获取当按钮文本
        current_text = self.select_all_button.text()
        
        # 遍历所有行的复选框
        all_checked = True  # 用于检查是否所框都已选中
        for row in range(self.table.rowCount()):
            checkbox_widget = self.table.cellWidget(row, 0)
            if checkbox_widget:
                checkbox = checkbox_widget.layout().itemAt(0).widget()
                if not checkbox.isChecked():
                    all_checked = False
                    break
        
        # 根据当前状态切换
        if all_checked:
            # 如果当前全部选中，则取消全选
            for row in range(self.table.rowCount()):
                checkbox_widget = self.table.cellWidget(row, 0)
                if checkbox_widget:
                    checkbox = checkbox_widget.layout().itemAt(0).widget()
                    checkbox.setChecked(False)
            self.select_all_button.setText("全选")
        else:
            # 如果当前未全选，则全选
            for row in range(self.table.rowCount()):
                checkbox_widget = self.table.cellWidget(row, 0)
                if checkbox_widget:
                    checkbox = checkbox_widget.layout().itemAt(0).widget()
                    checkbox.setChecked(True)
            self.select_all_button.setText("取消全选")

    def update_theme(self, theme_name):
        """更新主题"""
        self.current_theme = theme_name
        # 设置文字颜色
        text_color = "white" if theme_name == "dark" else "black"
        
        # 更新所有标签的文字颜色
        self.vendor_filter_label.setStyleSheet(f"color: {text_color};")
        self.device_type_filter_label.setStyleSheet(f"color: {text_color};")
        self.host_filter_label.setStyleSheet(f"color: {text_color};")
        self.ip_filter_label.setStyleSheet(f"color: {text_color};")
        self.online_status_filter_label.setStyleSheet(f"color: {text_color};")
        self.time_label.setStyleSheet(f"color: {text_color};")
        
        # 更新下拉框的文字颜色
        combo_style = f"""
            QComboBox {{
                color: {text_color};
                background-color: transparent;
                border: 1px solid #ccc;
            }}
            QComboBox:hover {{
                border: 1px solid #aaa;
            }}
            QComboBox QAbstractItemView {{
                color: {text_color};
                background-color: {self.palette().base().color().name()};
                selection-background-color: #0078d7;
            }}
        """
        self.vendor_filter_combo.setStyleSheet(combo_style)
        self.device_type_filter_combo.setStyleSheet(combo_style)
        self.online_status_filter_combo.setStyleSheet(combo_style)
        self.time_combo.setStyleSheet(combo_style)

    def apply_filter(self):
        """应用筛选条件"""
        vendor_filter = self.vendor_filter_combo.currentText()
        device_type_filter = self.device_type_filter_combo.currentText()
        host_filter = self.host_filter_input.text().strip().lower()
        ip_filter = self.ip_filter_input.text().strip().lower()
        online_status_filter = self.online_status_filter_combo.currentText()
        
        if vendor_filter == "全部":
            vendor_filter = ""
        if device_type_filter == "全部":
            device_type_filter = ""
        if online_status_filter == "全部":
            online_status_filter = ""
            
        # 保存当前页码
        old_page = self.current_page
        
        # 计算新的筛选结果
        filtered_indices = [
            i for i, device in enumerate(self.devices) if
            (not vendor_filter or vendor_filter == device["vendor"]) and
            (not device_type_filter or device_type_filter == device["device_type"]) and
            (not host_filter or host_filter in device["host"].lower()) and
            (not ip_filter or ip_filter in device["ip"].lower()) and
            (not online_status_filter or online_status_filter == device["online"])
        ]
        
        # 更新筛选索引
        self.filtered_indices = filtered_indices
        
        # 计算新的总页数
        total_devices = len(filtered_indices) if filtered_indices else len(self.devices)
        total_pages = (total_devices + self.page_size - 1) // self.page_size
        
        # 如果旧页码超出新的总页数，则设置为最后一页
        # 否则保持原页码
        if old_page > total_pages:
            self.current_page = total_pages if total_pages > 0 else 1
        else:
            self.current_page = old_page
        
        # 更新表格显示
        self.update_table()

    # def port_config(self):
    #     # 获取当前选中的设备信息
    #     selected_devices = []
    #     for row in range(self.table.rowCount()):
    #         checkbox_widget = self.table.cellWidget(row, 0)
    #         if checkbox_widget and checkbox_widget.layout().itemAt(0).widget().isChecked():
    #             device_info = {
    #                 "vendor": self.table.item(row, 1).text(),
    #                 "host": self.table.item(row, 4).text(),
    #                 "model": self.table.item(row, 3).text(),
    #                 "ip": self.table.item(row, 5).text()
    #             }
    #             selected_devices.append(device_info)
        
    #     # 如果选中了多个设备，提示能选择一个
    #     if len(selected_devices) > 1:
    #         QMessageBox.warning(self, "警告", "请只选择一个设备进行端口配置")
    #         return
        
        # 如果没有选择设备，则正常打开端口配置窗口
        # if not selected_devices:
        #     # from Portconfig.portconfig import port_config
        #     port_config(self)
        #     return
        
        # # 如果选择了一个设备，则打开端口配置窗口并自动选择该设备
        # # from Portconfig.portconfig import port_config
        # dialog = port_config(self)
        
        # 设置选中的设备信息
        # device = selected_devices[0]
        # dialog.vendor_combo.setCurrentText(device["vendor"])
        # dialog.hostname_combo.setCurrentText(device["host"])
        # dialog.model_combo.setCurrentText(device["model"])
        # dialog.ip_combo.setText(device["ip"])

    def update_page_info(self):
        """更新分页信息"""
        devices_to_show = [self.devices[i] for i in self.filtered_indices] if self.filtered_indices else self.devices
        total_devices = len(devices_to_show)
        total_pages = (total_devices + self.page_size - 1) // self.page_size
        
        # 确保当前页码有效
        if self.current_page > total_pages:
            self.current_page = total_pages if total_pages > 0 else 1
        
        # 更新页码信息
        self.page_info_label.setText(f"第 {self.current_page} / {total_pages} 页 (共 {total_devices} 条)")
        
        # 更新按钮状态
        self.prev_page_btn.setEnabled(self.current_page > 1)
        self.next_page_btn.setEnabled(self.current_page < total_pages)
        
        return devices_to_show

    def get_current_page_devices(self, devices_to_show):
        """获取当前页的设备列表"""
        start_idx = (self.current_page - 1) * self.page_size
        end_idx = start_idx + self.page_size
        return devices_to_show[start_idx:end_idx]

    def prev_page(self):
        """上一页"""
        if self.current_page > 1:
            self.current_page -= 1
            self.update_table()

    def next_page(self):
        """下一页"""
        devices_to_show = [self.devices[i] for i in self.filtered_indices] if self.filtered_indices else self.devices
        total_pages = (len(devices_to_show) + self.page_size - 1) // self.page_size
        if self.current_page < total_pages:
            self.current_page += 1
            self.update_table()

    def goto_page(self):
        """跳转到指定页"""
        try:
            page = int(self.goto_page_input.text())
            devices_to_show = [self.devices[i] for i in self.filtered_indices] if self.filtered_indices else self.devices
            total_pages = (len(devices_to_show) + self.page_size - 1) // self.page_size
            
            if 1 <= page <= total_pages:
                self.current_page = page
                self.update_table()
            else:
                QMessageBox.warning(self, "警告", f"页码必须在 1 到 {total_pages} 之间")
        except ValueError:
            QMessageBox.warning(self, "警告", "请输入有效的页码")

    def change_page_size(self, size):
        """改变每页显示的数量"""
        self.page_size = int(size)
        self.current_page = 1  # 重置到第一页
        self.update_table()

    def show_import_result(self, imported_count, vendor_stats, duplicate_ips):
        """显示导入结果的对话框"""
        dialog = QDialog(self)
        dialog.setWindowTitle("导入结果")
        dialog.setMinimumWidth(400)
        dialog.setMinimumHeight(300)
        
        # 创建主布局
        layout = QVBoxLayout(dialog)
        
        # 创建滚动区域
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        
        # 创建内容容器
        content_widget = QWidget()
        content_layout = QVBoxLayout(content_widget)
        
        # 添加导入统计信息
        if imported_count > 0:
            # 添加总计信息
            total_label = QLabel(f"总计导入：{imported_count} 台设备")
            total_label.setStyleSheet("font-weight: bold; font-size: 14px;")
            content_layout.addWidget(total_label)
            
            # 添加厂商统计信息
            vendor_group = QGroupBox("各厂商设备数量")
            vendor_layout = QVBoxLayout()
            for vendor, count in vendor_stats.items():
                vendor_label = QLabel(f"{vendor}：{count} 台")
                vendor_layout.addWidget(vendor_label)
            vendor_group.setLayout(vendor_layout)
            content_layout.addWidget(vendor_group)
        
        # 添加重复IP信息
        if duplicate_ips:
            duplicate_group = QGroupBox("重复IP设备列表")
            duplicate_layout = QVBoxLayout()
            for device in duplicate_ips:
                duplicate_label = QLabel(f"• {device}")
                duplicate_layout.addWidget(duplicate_label)
            duplicate_group.setLayout(duplicate_layout)
            content_layout.addWidget(duplicate_group)
        
        # 如果没有成功导入任何设备
        if imported_count == 0:
            if duplicate_ips:
                msg_label = QLabel("所有设备均为重复IP，导入失败。")
            else:
                msg_label = QLabel("没有新设备被导入")
            msg_label.setStyleSheet("color: red; font-weight: bold;")
            content_layout.addWidget(msg_label)
        
        # 添加弹性空间
        content_layout.addStretch()
        
        # 将内容容器添加到滚动区域
        scroll_area.setWidget(content_widget)
        layout.addWidget(scroll_area)
        
        # 添加确定按钮
        button_box = QDialogButtonBox(QDialogButtonBox.Ok)
        button_box.accepted.connect(dialog.accept)
        layout.addWidget(button_box)
        
        # 设置对话框样式
        dialog.setStyleSheet("""
            QDialog {
                background-color: white;
            }
            QGroupBox {
                border: 1px solid #cccccc;
                border-radius: 5px;
                margin-top: 10px;
                font-weight: bold;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 3px;
            }
            QLabel {
                padding: 5px;
            }
            QPushButton {
                padding: 5px 15px;
                background-color: #1296db;
                color: white;
                border: none;
                border-radius: 3px;
            }
            QPushButton:hover {
                background-color: #108ecf;
            }
        """)
        
        # 显示对话框
        dialog.exec_()

    def show_column_selection(self):
        """显示列选择对话框"""
        dialog = ColumnSelectionDialog(self)
        
        # 设置当前列的显示状态
        for key, checkbox in dialog.checkboxes.items():
            if key in self.column_keys:
                column_index = self.column_keys.index(key)
                checkbox.setChecked(not self.table.isColumnHidden(column_index))
        
        # 显示对话框
        if dialog.exec() == QDialog.Accepted:
            selected_columns = dialog.get_selected_columns()
            self.update_table_with_selected_columns(selected_columns)
            
            # 保存选择到数据库
            try:
                with sqlite3.connect('device_config/config_file/device_info.db') as conn:
                    cursor = conn.cursor()
                    
                    # 删除旧表（如果存在）
                    cursor.execute('DROP TABLE IF EXISTS column_selection')
                    
                    # 创建新表
                    cursor.execute('''
                        CREATE TABLE column_selection (
                            column_name TEXT PRIMARY KEY,
                            is_visible INTEGER
                        )
                    ''')
                    
                    # 插入新的选择
                    for column, is_visible in selected_columns.items():
                        cursor.execute(
                            'INSERT INTO column_selection (column_name, is_visible) VALUES (?, ?)',
                            (column, 1 if is_visible else 0)
                        )
                    
                    conn.commit()
            except sqlite3.Error as e:
                QMessageBox.warning(self, "警告", f"保存列选择失败: {str(e)}")

    def update_table_with_selected_columns(self, selected_columns):
        """根据选择的列更新表格显示"""
        # 保存当前选择的列
        for key, is_selected in selected_columns.items():
            if key in self.column_keys:
                column_index = self.column_keys.index(key)
                self.table.setColumnHidden(column_index, not is_selected)
        
        # 调整列宽
        self.table.resizeColumnsToContents()

    def load_saved_column_selection(self):
        """加载保存的列选择设置"""
        try:
            with sqlite3.connect('device_config/config_file/device_info.db') as conn:
                cursor = conn.cursor()
                
                # 检查表是否存在
                cursor.execute('''
                    SELECT name FROM sqlite_master 
                    WHERE type='table' AND name='column_selection'
                ''')
                
                if cursor.fetchone():
                    # 加载保存的设置
                    cursor.execute('SELECT column_name, is_visible FROM column_selection')
                    saved_selections = {row[0]: bool(row[1]) for row in cursor.fetchall()}
                    
                    # 应用设置
                    if saved_selections:
                        self.update_table_with_selected_columns(saved_selections)
        except sqlite3.Error as e:
            print(f"加载列选择设置失败: {str(e)}")

    def create_action_buttons(self, row):
        """创建操作按钮"""
        button_widget = QWidget()
        button_layout = QHBoxLayout(button_widget)
        button_layout.setContentsMargins(5, 0, 5, 0)
        button_layout.setSpacing(5)
        
        # 获取管理员权限状态
        is_admin = self.check_is_admin()
        
        # 编辑按钮
        edit_btn = QPushButton("编辑")
        edit_btn.setStyleSheet("""
            QPushButton {
                background-color: #409EFF;
                color: white;
                border: none;
                border-radius: 4px;
                padding: 5px 10px;
            }
            QPushButton:hover {
                background-color: #66B1FF;
            }
            QPushButton:disabled {
                background-color: #A0CFFF;
                color: #FFFFFF;
            }
        """)
        edit_btn.clicked.connect(lambda: self.edit_device(row))
        edit_btn.setEnabled(is_admin)  # 根据权限设置是否可用
        if not is_admin:
            edit_btn.setToolTip("需要管理员权限")
        button_layout.addWidget(edit_btn)
        
        # 删除按钮
        delete_btn = QPushButton("删除")
        delete_btn.setStyleSheet("""
            QPushButton {
                background-color: #F56C6C;
                color: white;
                border: none;
                border-radius: 4px;
                padding: 5px 10px;
            }
            QPushButton:hover {
                background-color: #F78989;
            }
            QPushButton:disabled {
                background-color: #FAB6B6;
                color: #FFFFFF;
            }
        """)
        delete_btn.clicked.connect(lambda: self.delete_single_device(row))
        delete_btn.setEnabled(is_admin)  # 根据权限设置是否可用
        if not is_admin:
            delete_btn.setToolTip("需要管理员权限")
        button_layout.addWidget(delete_btn)
        
        # 添加弹性空间
        button_layout.addStretch()
        
        return button_widget

    def edit_device(self, row):
        """编辑设备"""
        if not self.check_is_admin():
            QMessageBox.warning(self, "权限不足", "只有管理员可以编辑设备,请点击右上角的个人中心登录")
            return
            
        # ... 原有的编辑设备代码 ...

    def delete_single_device(self, row):
        """删除单个设备"""
        if not self.check_is_admin():
            QMessageBox.warning(self, "权限不足", "只有管理员可以删除单个设备,请点击右上角的个人中心登录")
            return
            
        # ... 原有的删除设备代码 ...

    def create_buttons(self):
        """创建所有按钮"""
        # 创建底部按钮布局
        self.bottom_buttons_layout = QHBoxLayout()
        self.layout.addLayout(self.bottom_buttons_layout)
        
        self.edit_data_button = QPushButton("编辑数据")
        self.edit_data_button.setIcon(
            QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.edit_icon_base64)))))
        self.edit_data_button.setCursor(Qt.PointingHandCursor)
        self.edit_data_button.clicked.connect(self.edit_data)
        self.bottom_buttons_layout.addWidget(self.edit_data_button)
        
        # 创建全选按钮
        self.select_all_button = QPushButton("全选")
        self.select_all_button.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.select_all_icon_base64)))))
        self.select_all_button.setCursor(Qt.PointingHandCursor)
        self.select_all_button.clicked.connect(self.toggle_select_all)
        self.bottom_buttons_layout.addWidget(self.select_all_button)

        # 创建并初始化所有需要权限控制的按钮 - 移除初始的 setEnabled(False)
        self.batch_delete_btn = QPushButton("批量删除")
        self.batch_delete_btn.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.delete_icon_base64)))))
        self.batch_delete_btn.setCursor(Qt.PointingHandCursor)
        self.batch_delete_btn.clicked.connect(lambda: delete_device(self))
        self.bottom_buttons_layout.addWidget(self.batch_delete_btn)

        self.import_btn = QPushButton("导入设备")
        self.import_btn.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.import_icon_base64)))))
        self.import_btn.setCursor(Qt.PointingHandCursor)
        self.import_btn.clicked.connect(lambda: import_devices(self))
        self.bottom_buttons_layout.addWidget(self.import_btn)

        self.add_btn = QPushButton("添加设备")
        self.add_btn.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.add_icon_base64)))))
        self.add_btn.setCursor(Qt.PointingHandCursor)
        self.add_btn.clicked.connect(lambda: show_add_device_dialog(self))
        self.bottom_buttons_layout.addWidget(self.add_btn)

        # self.port_config_btn = QPushButton("端口配置")
        # self.port_config_btn.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.port_config_icon_base64)))))
        # self.port_config_btn.setCursor(Qt.PointingHandCursor)
        # self.port_config_btn.clicked.connect(self.port_config)
        # self.bottom_buttons_layout.addWidget(self.port_config_btn)

        self.discovery_button = QPushButton("自动发现")
        self.discovery_button.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.auto)))))
        self.discovery_button.setCursor(Qt.PointingHandCursor)
        self.discovery_button.clicked.connect(self.show_discovery_dialog)
        self.bottom_buttons_layout.addWidget(self.discovery_button)

        # 预缓存图标
        self.online_icon = QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.online_icon_base64))))
        self.offline_icon = QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.offline_icon_base64))))
        
        # 添加分页控件布局
        self.pagination_layout = QHBoxLayout()
        
        # 添加页码显示和控制
        self.prev_page_btn = QPushButton("上一页")
        self.prev_page_btn.clicked.connect(self.prev_page)
        self.prev_page_btn.setFixedWidth(60)
        
        self.next_page_btn = QPushButton("下一页")
        self.next_page_btn.clicked.connect(self.next_page)
        self.next_page_btn.setFixedWidth(60)
        
        self.page_info_label = QLabel()
        
        self.goto_page_input = QLineEdit()
        self.goto_page_input.setFixedWidth(50)
        self.goto_page_input.setValidator(QIntValidator(1, 99999))
        
        self.goto_page_btn = QPushButton("跳转")
        self.goto_page_btn.clicked.connect(self.goto_page)
        self.goto_page_btn.setFixedWidth(50)
        
        self.page_size_combo = QComboBox()
        self.page_size_combo.addItems(["20", "50", "100", "200"])
        self.page_size_combo.setCurrentText(str(self.page_size))
        self.page_size_combo.currentTextChanged.connect(self.change_page_size)
        
        # 将分页控件添加到布局
        self.pagination_layout.addWidget(self.prev_page_btn)
        self.pagination_layout.addWidget(self.page_info_label)
        self.pagination_layout.addWidget(self.next_page_btn)
        self.pagination_layout.addWidget(QLabel("跳转到:"))
        self.pagination_layout.addWidget(self.goto_page_input)
        self.pagination_layout.addWidget(self.goto_page_btn)
        self.pagination_layout.addWidget(QLabel("每页显示:"))
        self.pagination_layout.addWidget(self.page_size_combo)
        self.pagination_layout.addStretch()
        
        # 将分页布局添加到主布局
        self.layout.addLayout(self.pagination_layout)
        
        # 加载设备数据
        self.load_devices()

        self.timer = QTimer()
        self.timer.timeout.connect(self.start_ping_thread)
        self.last_device_info = {}
        self.load_column_selection()

        # 第一次运行时不等待五分钟
        self.first_run = True
        self.start_ping_thread()

        # # 添加自定义时间设置
        # self.custom_time_layout = QHBoxLayout()

        # self.time_label = QLabel("轮巡时间:")
        # self.custom_time_layout.addWidget(self.time_label)

        # self.time_combo = QComboBox()
        # self.time_combo.addItems(["5分钟", "10分钟", "15分钟", "自定义"])
        # self.time_combo.currentTextChanged.connect(self.set_timer_from_combo)
        # self.custom_time_layout.addWidget(self.time_combo)

        # self.export_button = QPushButton("导出")
        # self.export_button.setIcon(QIcon(QPixmap.fromImage(QImage.fromData(bytes.fromhex(icons.export)))))
        # self.export_button.setCursor(Qt.PointingHandCursor)
        # self.export_button.clicked.connect(self.export_to_excel)
        # self.bottom_buttons_layout.addWidget(self.export_button)

        # self.custom_time_input = QLineEdit()
        # self.custom_time_input.setPlaceholderText("输入钟数")
        # self.custom_time_input.setMaxLength(3)
        # self.custom_time_input.setValidator(QIntValidator())
        # self.custom_time_input.setEnabled(False)  # 初始时禁用
        # self.custom_time_input.textChanged.connect(self.validate_custom_time)
        # self.custom_time_layout.addWidget(self.custom_time_input)
        # self.custom_time_layout.addStretch(1)
        # # self.custom_time_layout.addSpacing(50)  # 空白占位符

        # # 将轮巡时间设置放在 "SecureCRT设置" 按钮的右边
        # self.bottom_buttons_layout.addLayout(self.custom_time_layout)

        # 默认选择5分钟
        # self.time_combo.setCurrentIndex(0)

        self.filtered_indices = []
        self.load_devices()
        self.timer = QTimer()
        self.timer.timeout.connect(self.start_ping_thread)
        self.last_device_info = {}
        self.load_column_selection()

        self.first_run = True
        self.start_ping_thread()

        # 加载外部样式表
        self.load_styles()

        # 添加一个专门用于自动导出的定时器
        self.export_timer = QTimer()
        self.export_timer.timeout.connect(self.perform_auto_export)

        # 确保数据库目录存在
        Path("device_config/config_file").mkdir(parents=True, exist_ok=True)
        
        # 初始化数据库连接
        self.db_path = "device_config/config_file/device_info.db"
        self.init_config_db()
        
        # 加载配置
        self.load_config()
        
        # 优化表格性能
        self.table.setVerticalScrollMode(QTableWidget.ScrollPerPixel)
        self.table.setHorizontalScrollMode(QTableWidget.ScrollPerPixel)
        self.table.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table.setSelectionBehavior(QTableWidget.SelectRows)
        self.table.setSelectionMode(QTableWidget.SingleSelection)
        self.table.verticalHeader().setVisible(False)
        self.table.horizontalHeader().setStretchLastSection(True)
        self.table.setShowGrid(False)  # 可选：隐藏网格线以提高性能

        # 添加设备信息队列
        self.device_queue = deque()
        self.is_processing = False
        
        # 连接信号到槽
        self.update_device_signal.connect(self.update_single_device)
        self.batch_update_signal.connect(self.update_device_batch)
        
        # 创建事件循环
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)
        
        # 创建线程池
        self.thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=4)
    
        # 在创建表格后，加载保存的列选择设置
        self.load_saved_column_selection()
    
        # 添加权限检查方法
        self.check_admin_permissions()

    def resizeEvent(self, event):
        """重写调整大小事件以更新 size grip 位置"""
        super().resizeEvent(event)
        # 更新 size grip 位置到右下角
        self._size_grip.move(
            self.width() - self._size_grip.width(),
            self.height() - self._size_grip.height()
        )
